#ifndef _M_MATCHER_H__
#define _M_MATCHER_H__

#include "util.hpp"
#include "online.hpp"
#include "db.hpp"
#include "room.hpp"
#include <list>
#include <mutex>
#include <condition_variable>

template<class T>
class match_queue
{
    private:
        std::list<T> _list;// 用list而不用queue是因为我们有删除中间数据的需要
        std::mutex _mutex;// 实现线程安全
        std::condition_variable _cond;// 这个条件变量主要是为了阻塞消费者，队列元素<2则阻塞
    public:
        // 获取元素个数
        int size() 
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.size();
        }
        
        // 判断是否为空
        bool empty()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.empty();
        }

        // 阻塞线程
        void wait()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _cond.wait(lock);
        }

        // 入队数据，并唤醒线程
        void push(const T& data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.push_back(data);
            _cond.notify_all();
        }

        // 出队数据
        bool pop(T& data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_list.empty() == true) 
            {
                return false;
            }
            data = _list.front();
            _list.pop_front();
            return true;
        }

        void remove(T& data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.remove(data);
        }
};

class matcher
{
    private:
        match_queue<uint64_t> _q_bronze;// 青铜段位
        match_queue<uint64_t> _q_silver;// 白银段位
        match_queue<uint64_t> _q_gold;// 黄金段位
        // 三个不同段位匹配队列的处理线程
        std::thread _th_bronze;
        std::thread _th_silver;
        std::thread _th_gold;
        room_manager* _rm;
        user_table* _ut;
        online_manager* _om;

    private:
        void handle_match(match_queue<uint64_t>& mq)
        {
            while(1)
            {
                // 1.判断队列人数是否大于2，小于2则阻塞等待
                while(mq.size() < 2)
                {
                    mq.wait();
                }
                // 2.到此就代表人数够了，出队两个玩家
                uint64_t uid1,uid2;
                bool ret = mq.pop(uid1);
                if(ret == false) { continue; }
                ret = mq.pop(uid2);
                if(ret == false)
                {
                    // 到这还需要重新把uid1放入队列中继续匹配
                    this->add(uid1);
                    continue;
                }
                // 3.校验两个玩家是否在线，若有人掉线，需要把另一人重新添加到队列
                wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
                if(conn1.get() == nullptr)
                {
                    this->add(uid2);
                    continue;
                }
                wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
                if(conn2.get() == nullptr)
                {
                    this->add(uid1);
                    continue;
                }
                // 4.为两个玩家创建房间，并将玩家加入房间
                room::room_ptr rp = _rm->create_room(uid1,uid2);
                if(rp.get() == nullptr)
                {
                    this->add(uid1);
                    this->add(uid2);
                    continue;
                }
                // 5.对两个玩家分别进行响应
                Json::Value resp;
                resp["optype"] = "match_success";
                resp["result"] = true;
                std::string body;
                json_util::serialize(resp, body);
                conn1->send(body);
                conn2->send(body);
            }
        }

        void th_bronze_entry()
        {
            handle_match(_q_bronze);
        }

        void th_silver_entry()
        {
            handle_match(_q_silver);
        }

        void th_gold_entry()
        {
            handle_match(_q_gold);
        }

    public:
        matcher(room_manager* rm, user_table* ut, online_manager* om):
        _rm(rm),_ut(ut),_om(om),
        _th_bronze(std::thread(&matcher::th_bronze_entry,this)),
        _th_silver(std::thread(&matcher::th_silver_entry,this)),
        _th_gold(std::thread(&matcher::th_gold_entry,this))
        {
            DLOG("game matcher is initialized!");
        }

        bool add(u_int64_t uid)
        {
            // 根据玩家的天梯分数放入不同的匹配队列
            // 1.根据用户ID，获取玩家信息
            Json::Value user;
            bool ret = _ut->select_by_id(uid,user);
            if(ret == false)
            {
                DLOG("get player:%ld information failed!",uid);
                return false;
            }
            int score = user["score"].asInt();
            if(score < 2000)
            {
                _q_bronze.push(uid);
            }
            else if(score >= 2000 && score < 3000)
            {
                _q_silver.push(uid);
            }
            else
            {
                _q_gold.push(uid);
            }
            return true;
        }

        bool del(u_int64_t uid)
        {
            Json::Value user;
            bool ret = _ut->select_by_id(uid,user);
            if(ret == false)
            {
                DLOG("get player:%ld information failed!",uid);
                return false;
            }
            int score = user["score"].asInt();
            if(score < 2000)
            {
                _q_bronze.remove(uid);
            }
            else if(score >= 2000 && score < 3000)
            {
                _q_silver.remove(uid);
            }
            else
            {
                _q_gold.remove(uid);
            }
            return true;
        }

};

#endif

